/** \file    Segmentation.cpp
  * \brief   Segmentation of an image into pixelwise regions that stores among others 
  *			 connectivity of regions.
  * \date    2001 - 2003
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/segm/src/Segmentation.cpp,v 1.1 2004/05/20 09:11:46 cscom1r4 Exp $
  */

#include <algorithm>
#include <stdexcept>

#include "imglib/convert.h"
#include "imglib/segmbasic.h"
#include "imglib/colorate.h"
#include "imglib/bitmap.h"

#include "segm/Segmentation.h"
#include "segm/PixelIterator.h"

Segmentation::Segmentation()
{
	clear();
}

void Segmentation::clear()
{
	m_segmentIvector.clear();
	m_segmentCoordMap.Clear();
	m_width = m_height = 0;
	m_sourceImage = 0;
	m_segmImageFile.erase();
	m_plotFileName.erase();
	changed();
}

void Segmentation::changed()
{
	m_positionsFound = false;
	m_colorStatisticsFound = false;
}

/**ignores black=(0,0,0) color*/
void Segmentation::create(const segm_image & source)
{
	unsigned w = m_width = source.width();
	unsigned h = m_height = source.height();
	m_segmentIvector.clear();
	m_segmentCoordMap.Resize(w,h,-1);
	unsigned x,y;

	for (x = 0; x != w; ++x) {
		for (y = 0; y != h; ++y) {
			int p = color2int(source(x,y));
			if( p == 0 ) continue;
			int i = newSegmentByColor( p, x, y );
			++m_segmentIvector[i].pixelCount;
			if( x+1 != w ) {
				int p1 = color2int(source(x+1,y));
				if( p1 != 0 && p1 != p ) {
					size_t i1 = newSegmentByColor( p1, x+1, y );
					m_segmentIvector[i].neighbours[p1] = i1;
					m_segmentIvector[i1].neighbours[p] = i;
				}
			}
			if( y+1 != h ) {
				int p1 = color2int(source(x,y+1));
				if( p1 != 0 && p1 != p ) {
					size_t i1 = newSegmentByColor( p1, x, y+1 );
					m_segmentIvector[i].neighbours[p1] = i1;
					m_segmentIvector[i1].neighbours[p] = i;
				}
			}
		}
	}
	changed();
}

void Segmentation::create(segm_image &s, const byte_image &i)
{
	index_map imap(s.width(), s.height());
	img::relabel(imap, img::simple_checker<segm_image>(s));

	//here we need black color to represent out-of-mask again
	img::colorate(i, imap, s, img::BGRA<unsigned>(), 50, true);

	create(s);
}

void Segmentation::swap( Segmentation &str )
{
	m_segmentIvector.swap( str.m_segmentIvector );
	m_segmentCoordMap.Swap( str.m_segmentCoordMap );

	m_segmImageFile.swap(str.m_segmImageFile);

	simple_swap( m_width, str.m_width );
	simple_swap( m_height, str.m_height );

	simple_swap( m_sourceImage, str.m_sourceImage );

	simple_swap( m_positionsFound, str.m_positionsFound );
	simple_swap( m_colorStatisticsFound, str.m_colorStatisticsFound );
	simple_swap( m_upThrsInsteadDisp, str.m_upThrsInsteadDisp );

	m_plotFileName.swap(str.m_plotFileName);
}

double Segmentation::meanSegmentPixelCount() const
{
	if( count()==0 ) return 0;
	int s = 0;
	for (size_t i = 0; i != count(); ++i) {
		s += m_segmentIvector[i].pixelCount;
	}
	return double(s)/count();
}

double Segmentation::motionQuality() const
{
	double s = 0;
	for (size_t i = 0; i != count(); ++i)
		s += m_segmentIvector[i].profileVal();
	return s;
}

static inline void resetActive( Segment &g ) {
	g.active = false;
}

void Segmentation::deactivate() {
	std::for_each( segmentsBegin(), segmentsEnd(), resetActive );
}

static inline void setActive( Segment &g ) {
	g.active = true;
}

void Segmentation::activate() {
	std::for_each( segmentsBegin(), segmentsEnd(), setActive );
}

void Segmentation::noPredictedMotion() {
	iterator i;
	for( i = segmentsBegin(); i != segmentsEnd(); ++i ) {
		i->setPdisplacement(int_point(0,0));
	}
}

void Segmentation::checkMotionInterior(size_t maxRdispl) {
	PixelIterator p(*this, true);
	for (; p != PixelIterator(*this, false); ++p) {
		if (!p)
			continue;
		if (p.x() + p->pdisplacement().x + maxRdispl >= width() ||
			p.x() + p->pdisplacement().x < maxRdispl ||
			p.y() + p->pdisplacement().y + maxRdispl >= height() ||
			p.y() + p->pdisplacement().y < maxRdispl)
		{
			throw std::runtime_error("Mask is moved outside the frame by one of potential motion, please redefine the mask!");
		}
	}
}

/** boundingBox for segment should already be founded */
void Segmentation::segmentMask(int sg, img::bitmap & mask, unsigned ext) const
{
	const Segment &g = segmentByIndex( sg );
	mask.resize(g.boundingBox.width() + 2*ext, g.boundingBox.height() + 2*ext);
	if (ext > 0)
		mask.fill(false);
	int x,y, bx,by;
	for( y = g.boundingBox.y1, by = ext; y != g.boundingBox.y2; ++y, ++by ) {
		for( x = g.boundingBox.x1, bx = ext; x != g.boundingBox.x2; ++x, ++bx ) {
			mask.at(bx,by) = segmentIndex(x,y)==sg;
		}
	}
}

class ProfileResizer
{
public:
	ProfileResizer(unsigned size, bool activeOnly = false) : 
		m_size(size), m_activeOnly(activeOnly) {}
	void operator()(Segment & sg) {
		sg.profileResize((!m_activeOnly || sg.active) ? m_size : 0, 0);
	}
private:
	unsigned m_size;
	bool m_activeOnly;
};

void Segmentation::resizeProfiles(unsigned size, bool activeOnly)
{
	std::for_each(segmentsBegin(), segmentsEnd(), ProfileResizer(size, activeOnly));
}
